package com.pink.service.Impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.pink.component.RedisComponent;
import com.pink.entity.config.AppConfig;
import com.pink.entity.config.Constants;
import com.pink.entity.dto.UserDto;
import com.pink.entity.dto.VideoFileDto;
import com.pink.entity.query.VideoFilesQuery;
import com.pink.entity.query.VideoFileQuery;
import com.pink.entity.vo.request.VideoFileVo;
import com.pink.entity.vo.response.UploadingFileVo;
import com.pink.entity.vo.response.VideoFileLoadFileDataListVo;
import com.pink.exception.BusinessException;
import com.pink.mappers.VideoFileMapper;
import com.pink.service.VideoFileService;
import com.pink.utils.FFmpegUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import org.apache.commons.io.FileUtils;
import javax.annotation.Resource;
import java.io.File;
import java.io.RandomAccessFile;
import java.util.List;

@Service
@Slf4j
public class VideoFileServiceImpl extends ServiceImpl<VideoFileMapper, VideoFileDto> implements VideoFileService {

    @Resource
    RedisComponent redisComponent;

    @Resource
    FFmpegUtils ffmpegUtils;

    @Resource
    AppConfig appConfig;

    @Autowired
    VideoFileMapper videoFileMapper;

    @Override
    public String addVideoFile(UserDto user, Integer episodeNumber, Integer videoId, List<VideoFileVo> videoFileVo) {
        VideoFileQuery videoFileQuery = new VideoFileQuery();
        videoFileQuery.setEpisodeNumber(episodeNumber);
        videoFileQuery.setUserId(user.getUserId());
        videoFileQuery.setVideoId(videoId);
        for (VideoFileVo fileVo : videoFileVo) {
            videoFileQuery.setFileName(fileVo.getFileName());
            videoFileQuery.setUploadId(fileVo.getUploadId());
        }
        redisComponent.addFile2TransferQueue(videoFileQuery);
        return "添加成功";
    }

    @Override
    public VideoFilesQuery findFileQueryList(int pageNo, int pageSize, String title) {
        List<VideoFileLoadFileDataListVo> list = null;
        if(title == "" && title == null) {
            list = videoFileMapper.findFileQueryList();
        }else{
            list = videoFileMapper.findFileQueryList(title);
        }
        VideoFilesQuery videoFilesQuery = new VideoFilesQuery();
        videoFilesQuery.setPageNo(pageNo);
        videoFilesQuery.setPage(pageNo);
        videoFilesQuery.setPageSize(pageSize);
        videoFilesQuery.setTotalCount(list.size());
        if (list.size() >= 15 && list != null) {
            // 分页
            list = list.subList((pageNo - 1) * pageSize, pageNo * pageSize);
        }
        videoFilesQuery.setList(list);
        return videoFilesQuery;
    }

    @Override
    public List<VideoFileDto> getVideoFileList(Integer videoId) {
        return this.list(new LambdaQueryWrapper<VideoFileDto>().eq(VideoFileDto::getVideoId, videoId));
    }

    @Override
    public void transferVideoFile(VideoFileQuery videoFilequery) {
        VideoFileDto videoFileDto = new VideoFileDto();
        try {
            //处理流操作
            UploadingFileVo fileDto = redisComponent.getPreVideoFileInfo(videoFilequery.getUserId(), videoFilequery.getUploadId());
            if (fileDto == null) {
                throw new BusinessException("上传文件不存在");
            }
            // 获取临时文件路径
            String tempFilePath = appConfig.getProjectFolder() + Constants.FILE_FOLDER + Constants.FILE_FOllDER_TEMP + fileDto.getFilePath();
            File tempFile = new File(tempFilePath);
            // 获取目标文件路径
            String targetFilePath = appConfig.getProjectFolder() + Constants.FILE_FOLDER + Constants.FILE_VIDEO + fileDto.getFilePath();
            File targetFile = new File(targetFilePath);
            // 如果目标文件路径不存在，则创建目录和文件
            if (!targetFile.exists()) {
                targetFile.mkdirs();
            }
            // 将临时文件复制到目标文件
            FileUtils.copyDirectory(tempFile, targetFile);
            //删除临时文件
            FileUtils.forceDelete(tempFile);
            // 从redis中删除上传文件的信息
            redisComponent.delVideoFileInfo(videoFilequery.getUserId(), videoFilequery.getUploadId());
            //合并文件
            String completeVideo = targetFilePath+Constants.TEMP_VIDEO_NAME;
            this.union(targetFilePath,completeVideo,true);
            // 等待线程合并文件完成
            while (new File(completeVideo).length() == 0) {
                Thread.sleep(3000); // 等待3秒，直到文件大小不为0
            }
            // 获取播放时长
            Integer duration = ffmpegUtils.getVideoDuration(completeVideo);
            String fileType = ffmpegUtils.getFileType(completeVideo);
            String resolution = ffmpegUtils.getVideoResolution(completeVideo);
            long fileSize = new File(completeVideo).length();
            // 写入文件信息
            if(fileType == null&&resolution == ""){
                fileType = "mp4";
            }
            videoFileDto.setVideoId(videoFilequery.getVideoId());
            videoFileDto.setDuration(duration);
            videoFileDto.setFilePath(fileDto.getFilePath());
            videoFileDto.setEpisodeNumber(videoFilequery.getEpisodeNumber());
            videoFileDto.setStorageServer("127.0.0.1");
            videoFileDto.setLanguage("中文");
            videoFileDto.setResolution(resolution);
            videoFileDto.setFileSize(fileSize);
            videoFileDto.setFileType(fileType);
            //转成流媒体格式
            this.convertVideo2Ts(completeVideo);
        }catch (Exception e) {
            throw new BusinessException("文件上传失败");
        }
        // 写入数据库
        this.save(videoFileDto);
    }

    // 将视频文件转换为TS格式
    private void convertVideo2Ts(String completeVideo) {
        // 获取视频文件
        File videoFile = new File(completeVideo);
        // 获取视频文件的父目录
        File toFile = videoFile.getParentFile();
        // 获取视频文件的编码格式
        String codec = ffmpegUtils.getVideoCodec(completeVideo);
        // 如果视频文件的编码格式为HEVC
        if(Constants.VIDEO_CODEC_HEVC.equals(codec)){
            // 生成临时文件名
            String newFileName = completeVideo+Constants.VIDEO_CODEC_TEMP_FILE_SUFFIX;
            // 将视频文件重命名为临时文件
            new File(completeVideo).renameTo(new File(newFileName));
            // 将HEVC编码格式的视频文件转换为MP4格式
            ffmpegUtils.convertHevc2Mp4(newFileName,completeVideo);
            // 删除临时文件
            new File(newFileName).delete();
        }
        // 将视频文件转换为TS格式
        ffmpegUtils.convertVideo2Ts(toFile,completeVideo);
        // 删除视频文件
        videoFile.delete();

    }

    // 将视频文件合并为MP4格式并删除源文件
    private void union(String dirPath,String toPath,Boolean delSource) {
        //获取目录
        File dir = new File(dirPath);
        //判断目录是否存在
        if(!dir.exists()){
            throw new RuntimeException("目录不存在");
        }
        //获取目录下的所有文件
        File fileList[] = dir.listFiles();
        //获取目标文件
        File targetFile = new File(toPath);
        try(RandomAccessFile writeFile = new RandomAccessFile(targetFile,"rw")){
            //创建缓冲区
            byte[] buffer = new byte[1024 * 10];
            //遍历目录下的所有文件
            for(int i=0;i<fileList.length;i++){
                int len = -1;
                //创建
                File chumkFile = new File(dirPath+File.separator + i);
                RandomAccessFile readFile = new RandomAccessFile(chumkFile,"r");
                try{
                    //读取文件内容
                    while ((len = readFile.read(buffer)) != -1){
                        //写入目标文件
                        writeFile.write(buffer,0,len);
                    }
                }catch (Exception e){
                    log.error("合并文件异常",e);
                    throw new RuntimeException("合并文件异常");
                }finally {
                    //关闭文件
                    readFile.close();
                }
            }
        }catch (Exception e){
            log.error("合并文件异常",e);
        }finally {
            //删除源文件
            if(delSource){
                for(int i=0;i<fileList.length;i++){
                    fileList[i].delete();
                }
            }
        }
    }

}
